package taweb.flex.air.fexBrowser.display3d
{
	import alternativa.engine3d.controllers.CameraController;
	import alternativa.engine3d.controllers.FlyController;
	import alternativa.engine3d.controllers.ObjectController;
	import alternativa.engine3d.core.Camera3D;
	import alternativa.engine3d.core.Object3D;
	import alternativa.engine3d.core.Scene3D;
	import alternativa.engine3d.display.View;
	import alternativa.engine3d.events.MouseEvent3D;
	import alternativa.engine3d.materials.FillMaterial;
	import alternativa.engine3d.materials.SurfaceMaterial;
	import alternativa.engine3d.materials.TextureMaterial;
	import alternativa.engine3d.primitives.Box;
	import alternativa.engine3d.primitives.Cone;
	import alternativa.engine3d.primitives.GeoPlane;
	import alternativa.engine3d.primitives.GeoSphere;
	import alternativa.types.Point3D;
	import alternativa.types.Texture;
	import alternativa.utils.FPS;
	import alternativa.utils.KeyboardUtils;
	import alternativa.utils.MathUtils;
	
	import flash.display.BitmapData;
	import flash.display.BlendMode;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.net.URLRequest;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.ui.Keyboard;
	
	import mx.collections.ArrayList;
	import mx.controls.Alert;
	import mx.controls.HTML;
	import mx.controls.Text;
	import mx.core.Application;
	import mx.core.UIComponent;
	import mx.geom.RoundedRectangle;
	
	import taweb.flex.air.fexBrowser.tree.TreeItem;
	import taweb.flex.air.fexBrowser.tree.TreeLeaf;
	import taweb.flex.air.fexBrowser.tree.TreeNode;
	
	
	public class Navigator extends UIComponent {
		
		//Variable static pour les cubes
		private static var TAILLE_CUBE:int = 40;
		private static var MARGE_CUBE:int = 40;
		
		//Variable static pour les plaques
		private static var EPAISSEUR_PLAQUE:int = 30;
		private static var LONGUEUR_PLAQUE:int = 40;
		private static var LARGEUR_PLAQUE:int = 40;
		private static var MARGE_INTER_CUBE:int = 20;
		
		//Variable pour l'agencement des plaques
		
		private static var MAX_LONGEUR = 1000;
		private static var MAX_LARGEUR = 200;
		private static var MARGE_INTER_PLAQUE = 200;
		
		
		
		
		//variables indispensables à la creation d'une scene 3D
		private var scene:Scene3D;
		private var view:View;
		private var camera:Camera3D;
		private var cameraController:CameraController;
		private var objet3D:Array;
		private var html:HTML;
		
		//variables utils dans notre exemple
		private var environement:Environment;
		
		
		public function Navigator() {
			super();
			addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		public function init(pevt:Event): void {
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
			
			objet3D = new Array();
			
			// Creation de la scene
			scene = new Scene3D();
			scene.root = new Object3D();
			camera = new Camera3D();
			environement = new Environment();
			
			//Ajout de l'environnement
			environement.rotationZ = -0.34;
			environement.mobility = 410;
			scene.root.addChild(environement);
			
			//positionnement de la camera
			camera.x = 100;
			camera.y = -150;
			camera.z = 200;
			scene.root.addChild(camera);
			
			//creation et parametrage de la vue
			view = new View();
			view.interactive = true;
			
			addChild(view);
			view.camera = camera;
			
			// Connection du cameraController à la camera
			cameraController = new CameraController(stage);
			cameraController.camera = camera;
			cameraController.setDefaultBindings();
			cameraController.checkCollisions = true;
			cameraController.collisionRadius = 1;
			cameraController.controlsEnabled = true;
			cameraController.speed = 500;
			cameraController.zoomMultiplier = 0;
			cameraController.unbindAll();
			cameraController.bindKey(Keyboard.SHIFT,ObjectController.ACTION_ACCELERATE);
			cameraController.bindKey(KeyboardUtils.Z, ObjectController.ACTION_FORWARD);
			cameraController.bindKey(KeyboardUtils.S, ObjectController.ACTION_BACK);
			cameraController.bindKey(KeyboardUtils.Q, ObjectController.ACTION_LEFT);
			cameraController.bindKey(KeyboardUtils.D, ObjectController.ACTION_RIGHT);
			
			
			
			//ajout des écouteurs
			
			stage.addEventListener(Event.RESIZE, onResize);
			stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
			onResize(null);
			
			
		}
		
		public function refresh(treenode:TreeItem):void{
			//On detruit les ancien objet present sur la scene
			for each (var obj:DomainBox in objet3D)
			{
				obj.supprimerAllLink();
				scene.root.removeChild(obj);
			}
			
			while (objet3D.length!=0){
				objet3D.pop();
			}
			//Calcul des tailles de floor			
			var dom:Array = extractionNoeud(treenode);
			var tailleFloor:Array = new Array();
			for each (var dodo:Array in dom)
			{
				var temp:Array = dodo[1] as Array;
				tailleFloor.push(calculTailleFloor(temp.length,Navigator.TAILLE_CUBE,Navigator.MARGE_INTER_CUBE,Navigator.MARGE_CUBE));
			}
			//On cacul les position des differentes plaques en fonction des domaines
			var placementFloor:Array = placementFloor(tailleFloor,Navigator.EPAISSEUR_PLAQUE,Navigator.TAILLE_CUBE,Navigator.MAX_LONGEUR,Navigator.MAX_LARGEUR,Navigator.MARGE_INTER_PLAQUE);
			for (var o:int = 0;o<tailleFloor.length;o++){
				var colorCode:String = TreeNode (dom[o][0]).color;
				var floora:DomainBox = new DomainBox(tailleFloor[o][0],tailleFloor[o][1],Navigator.EPAISSEUR_PLAQUE,colorCode);
				var nodez:TreeNode = TreeNode (dom[o][0]);
				
				floora.z = placementFloor[o][2];
				floora.x = placementFloor[o][0];
				floora.y = placementFloor[o][1];
				
				objet3D.push(floora);
				
				if (html.location.indexOf(nodez.name)!=-1){
					cameraController.lookAt(floora.coords);
				}
				var temp:Array = dom[o][1] as Array;
				//On calcul les positions des cubes sur leur plaque
				var test1:Array = placementCubeOnFloor(temp.length,Navigator.TAILLE_CUBE,Navigator.MARGE_INTER_CUBE,Navigator.MARGE_CUBE,tailleFloor[o][1],tailleFloor[o][0],Navigator.EPAISSEUR_PLAQUE);
				for (var j:int = 0;j<test1.length;j++){
					var leaf:TreeLeaf = TreeLeaf (dom[o][1][j]);
					var node:TreeNode = TreeNode (dom[o][0]);
					if (leaf != null && node!=null){
						var decoupText:Array = new Array();
						decoupText = leaf.name.split(";  >>> ");
						var cube1:LinkBox = new LinkBox(Navigator.TAILLE_CUBE,Navigator.TAILLE_CUBE,Navigator.TAILLE_CUBE,decoupText[1],decoupText[0],node.name);
						floora.ajouterLink(cube1,test1[j][0],test1[j][1],test1[j][2]);
						cube1.addEventListener(MouseEvent3D.CLICK,onMouseClickLink);
					}
				}
				scene.root.addChild(floora);
			}
		}
		
		/*
		 * Handler qui change la page affiché lors du clique sur un cube 
		*/
		private function onMouseClickLink(evt:MouseEvent3D):void{
			var  box:LinkBox = evt.target as LinkBox;
			html.htmlLoader.load(new URLRequest(box.getLink));
		}
		
		/*
		 *Lors du redimenssionnement de l'application seul la largeur s'adapte 
		*/
		private function onResize(pevt:Event):void {
			view.width = stage.width;
			view.height = this.height;
		}
		
		/*
		 *Permet d'empecher la propagation de l'evenement onMouseWhell 
		*/
		private function onMouseWheel(evt:MouseEvent):void{
			evt.stopImmediatePropagation();
		}
		
		/*
		*@Description: Extrait les noeud d'un treenode
		*@param  	 : treenode:Le treenode
		*@return 	 : Tableau qui contient des tableaux de domaine a 0 et tableau de lien a 1
		*/
		protected function extractionNoeud(treenode:TreeItem):Array{
			var result:Array = new Array();
			for each (var tree:TreeItem in TreeNode (treenode).children)
			{
				var ss_result:Array = new Array();
				var links:Array = new Array();
				ss_result.push(tree,links);
				parcoursNoeud(tree,links);
				result.push(ss_result);
			}
			return result;
		}
		
		/*
		*@Description: Extrait les lien d'un treenode
		*@param  	 : treenode:Le treenode
		*@return 	 : Tableau qui contient des tableaux de domaine a 0 et tableau de lien a 1
		*/
		protected function parcoursNoeud(treenode:TreeItem,links:Array):void{
			var temp:TreeLeaf = treenode as TreeLeaf;
			if (temp!=null){
				links.push(temp);
			}else{
				for each (var tree:TreeItem in TreeNode (treenode).children)
				{
					parcoursNoeud(tree,links);
				}
			}
		}
		
		/*
		*@Description: Fonction qui calcul le placement des floors
		*@param  	 : floors:Le tableau des tailles des floor
		*@return 	 : Tableau qui contient par case des tableaux de coordonnees
		*/
		protected function placementFloor(floors:Array,epaisseurFloor:int,tailleCube:int,retourLimitLong:int,retourLimitLarg:int,margeFloor:int):Array{
			var result:Array = new Array();
			var i:int = 0;
			var cptLong:int = 0;
			var cptLarg:int = 0;
			var cptHaut:int = 0;
			
			
			for (i;i<floors.length;i++){
				var ss_result:Array = new Array();
				
				ss_result[0]=cptLarg;
				ss_result[1]=cptLong;
				ss_result[2]=cptHaut;
				
				if(cptLong>=retourLimitLong){
					cptLong=0;
					cptLarg+=floors[i][0]+floors[i-1][0]+margeFloor;
				}
				else if(cptLarg>=retourLimitLarg){
					cptLarg=0;
					cptLong=0;
					cptHaut+=2*(epaisseurFloor+tailleCube);
				}
				if (i!=0 && cptLong!=0){
					cptLong+=floors[i][1]+floors[i][1]/2+floors[i-1][1]+margeFloor;
				}else if (i!=floors.length-1){
					cptLong+=floors[i][1]+floors[i][1]/2+floors[i+1][1]+margeFloor;
				}else{
					cptLong+=floors[i][1]+floors[i][1]/2+floors[i][1]+margeFloor;
				}
				result.push(ss_result);
			}
			return result;
		}
		
		/*
		*@Description: Fonction qui calcul la taille du floor
		*@param  	 : nbLien:Nombre de lien a place
		*@param  	 : tailleCube:Taille des cube representant les liens
		*@param  	 : tailleMarge:Marge du floor
		*@param  	 : margeCube:Marge autour des cubes
		*@return 	 : Tableau qui contient à l'index 0 la largeur et à l'index 1 la longueur
		*/
		protected function calculTailleFloor(nbLien:int,tailleCube:int,tailleMarge:int,margeCube:int):Array{
			var result:Array = new Array();
			if (nbLien == 0){
				result[0]=tailleCube;
				result[1]=tailleCube;
			}
			else if(nbLien == 1){
				result[0]=tailleCube+2*tailleMarge;
				result[1]=tailleCube+2*tailleMarge;
			}
			else if(nbLien == 2){
				result[0]=nbLien*tailleCube+2*tailleMarge+nbLien*2*margeCube;
				result[1]=nbLien*tailleCube+2*tailleMarge+nbLien*2*margeCube;
			}
			else if(nbLien%3 == 0){
				result[0] = 3*tailleCube+2*tailleMarge+6*margeCube;
				result[1] = (nbLien/3)*tailleCube+2*tailleMarge+(nbLien/3)*2*margeCube;
			}
			else{
				result[0] = 3*tailleCube+2*tailleMarge+6*margeCube;
				result[1] = (((nbLien-nbLien%3)/3)+1)*tailleCube+2*tailleMarge+(((nbLien-nbLien%3)/3)+1)*2*margeCube;
			}
			return result;
		}
		
		
		/*
		*@Description: Fonction qui calcul les coordonnes de placement des cubes sur le floor
		*@param  	 : nbLien:Nombre de lien a place
		*@param  	 : tailleCube:Taille des cube representant les liens
		*@param  	 : tailleMarge:Marge du floor
		*@param  	 : margeCube:Marge autour des cubes
		*@return 	 : Tableau qui contient des tableaux contenant à l'index 0 le x, à l'index 1 le y et à l'index 2 le z  du placement de cube
		*/
		protected function placementCubeOnFloor(nbLien:int,tailleCube:int,tailleMarge:int,margeCube:int,longueurFloor:int,largeurFloor:int,epaisseurFloor:int):Array{
			var result:Array = new Array();
			if (nbLien == 0){
				var sub_result:Array = new Array();
				sub_result[0] = 0;
				sub_result[1] = 0;
				sub_result[2] = 0;
				result.push(sub_result);
			}
			else if(nbLien == 1){
				var sub_result:Array = new Array();
				sub_result[0] = 0;
				sub_result[1] = 0;
				sub_result[2]=epaisseurFloor/2+tailleCube/2;
				result.push(sub_result);
			}
			else if(nbLien == 2){
				var sub_result:Array = new Array();
				sub_result[0] = 0;
				sub_result[1] = (tailleMarge+tailleCube);
				sub_result[2]=epaisseurFloor/2+tailleCube/2;
				
				var sub_result2:Array = new Array();
				sub_result2[0] = 0;
				sub_result2[1] = -(tailleMarge+tailleCube);
				sub_result2[2]=epaisseurFloor/2+tailleCube/2;
				result.push(sub_result);
				result.push(sub_result2);
			}
			else if(nbLien%3 == 0){
				
				
				var x:int = -(largeurFloor/2)+ tailleMarge;
				var y:int = -(longueurFloor/2) + tailleMarge;
				var i:int = 0;
				var j:int = 0;
				for (i;i<nbLien/3;i++){
					if (i>0){
						y = y + tailleCube+(2*margeCube);
					}
					else{
						y = y + margeCube + tailleCube/2;
					}
					x = -(largeurFloor/2)+ tailleMarge;
					j=0;
					for (j;j<3;j++){
						var sub_result:Array = new Array();
						if (j>0){
							x = x + tailleCube+(2*margeCube);
						}else{
							x = x + margeCube + tailleCube/2;
						}
						sub_result[0]=x;
						sub_result[1]=y;
						sub_result[2]=epaisseurFloor/2+tailleCube/2;
						result.push(sub_result);
					}
				}
			}
			else{
				var x:int = -(largeurFloor/2)+ tailleMarge;
				var y:int = -(longueurFloor/2) + tailleMarge;
				var i:int = 0;
				var j:int = 0;
				var h:int =0;
				for (i;i<(nbLien/3);i++){
					if (i>0){
						y = y + tailleCube+(2*margeCube);
					}
					else{
						y = y + margeCube + tailleCube/2;
					}
					j=0;
					x = -(largeurFloor/2)+ tailleMarge;
					if (i == Math.round((nbLien/3))){
						h = nbLien%3;
					}else{
						h=3;
					}
					for (j;j<h;j++){
						var sub_result:Array = new Array();
						if (j>0){
							x = x + tailleCube+(2*margeCube);
						}else{
							x = x + margeCube + tailleCube/2;
						}
						sub_result[0]=x;
						sub_result[1]=y;
						sub_result[2]=epaisseurFloor/2+tailleCube/2;
						result.push(sub_result);
					}
				}
			}
			return result;
		}
		
		/*
		 * Fonction appelé à chaque image
		*/
		protected function onEnterFrame(pevt:Event):void {
			cameraController.processInput();
			scene.calculate();
		}
		
		public function set customHtml(html:HTML):void
		{
			this.html = html;
		}
	}
}